package com.example.signdemo.services.impl;

import com.example.core.libs.AppException;
import com.example.core.utils.RedisUtil;
import com.example.signdemo.services.IUserSignMService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;

@Service
public class UserSignMImpl implements IUserSignMService {
    @Autowired
    RedisUtil redisUtil;

    private final static String SIGN_KEY = "sign";

    /**
     * 签到
     *
     * @param userId
     */
    @Override
    public void sign(long userId) {
        LocalDate date = LocalDate.now();

        String key = buildSignKey(userId, date.getYear(), date.getMonthValue());
        if (checkIsSinged(key, date.getDayOfMonth())) {
            throw new AppException("该时间已经签到");
        }
        // date.getDayOfMonth() - 1 偏移量需要-1
        redisUtil.setBit(key, date.getDayOfMonth() - 1, true);
    }

    @Override
    public void againSign(long userId, String day) {
        LocalDate date;
        try {
            date = LocalDate.parse(buildYmd(LocalDate.now(), day), DateTimeFormatter.ISO_LOCAL_DATE);
        } catch (Exception e) {
            e.printStackTrace();
            throw new AppException("时间转换错误");
        }

        if (date.getDayOfMonth() >= LocalDate.now().getDayOfMonth()) {
            throw new AppException("补签日期错误");
        }

        String key = buildSignKey(userId, date.getYear(), date.getMonthValue());
        if (checkIsSinged(key, date.getDayOfMonth())) {
            throw new AppException("该时间已经签到");
        }
        redisUtil.setBit(key, date.getDayOfMonth() - 1, true);
    }

    /**
     * 获取当月连续签到次数
     *
     * @param userId
     * @return
     */
    @Override
    public int continuousSignCount(long userId) {
        LocalDate date = LocalDate.now();
        String key = buildSignKey(userId, date.getYear(), date.getMonthValue());

        List<Long> r = redisUtil.bitfieldGet(key, date.getDayOfMonth(), 0);

        int count = 0;
        if (!r.isEmpty()) {
            long v = r.get(0);

            // 方式一：转二进制字符串进行反转后遍历
//            StringBuffer b = new StringBuffer(Long.toBinaryString(v)).reverse();
//            for (int i = 0; i < b.length(); i++) {
//                if (b.charAt(i) == '1') {
//                    ++count;
//                } else {
//                    break;
//                }
//            }

            // 方式二：v & 1 进行判断，后  v >>= 1
            int len = Long.toBinaryString(v).length();
            for (int i = 0; i < len; i++) {
                if ((v & 1) == 1) {
                    ++count;
                    v >>= 1;
                } else {
                    break;
                }
            }
        }

        return count;
    }

    /**
     * 获取当月签到次数
     *
     * @param userId
     * @return
     */
    @Override
    public long signDays(long userId) {
        LocalDate date = LocalDate.now();
        String key = buildSignKey(userId, date.getYear(), date.getMonthValue());
        return redisUtil.bitCount(key);
    }

    /**
     * 构建key
     *
     * @param userId
     * @param year
     * @param month
     * @return
     */
    private String buildSignKey(long userId, int year, int month) {
        return String.format("%s:%s-%s:%s", SIGN_KEY, year, month, userId);
    }

    /**
     * 构建指定日的日期
     *
     * @param date
     * @param day
     * @return
     */
    private String buildYmd(LocalDate date, String day) {
        return String.format("%s-%s-%s", date.getYear(), date.getMonthValue(), day);
    }

    /**
     * 检查是否已经签到
     *
     * @param key
     * @return
     */
    private boolean checkIsSinged(String key, int day) {
        if (redisUtil.getBit(key, day - 1)) {
            return true;
        }
        return false;
    }
}
